/*
 * Copyright (C) 2003,2004 Christopher J. Stawarz <cstawarz@cfa.harvard.edu>
 *
 * This file is part of SLglade.
 *
 * SLglade is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * SLglade is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with SLglade; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */


#include <string.h>
#include <malloc.h>


/* Instead of writing our own closure mechanism, we use the one
 * from SLgtk.  These two functions come from slglib.c. */

extern GClosure* slg_closure_new(SLang_Name_Type *function, SLang_Any_Type **args,
                                 unsigned int nargs, SLang_Any_Type *swap_data);
extern int extract_slang_args(unsigned int nargs, SLang_Any_Type ***pargs);


typedef struct {
   SLang_Name_Type *function;	/* S-Lang function to handle signal */
   SLang_Any_Type **args;	/* Function args passed in from S-Lang scope */
   unsigned int  nargs;		/* Number of args */
} signal_connect_data;


static void
signal_connect_connect_func(const gchar *handler_name, GObject *object,
			    const gchar *signal_name, const gchar *signal_data,
			    GObject *connect_object, gboolean after,
			    gpointer user_data)
{
   /* glade_xml_signal_connect_full() and glade_xml_signal_autoconnect_full()
    * both pass NULL for signal_data, so there's no need to worry about it. */

   char *handler;
   signal_connect_data *data = (signal_connect_data *)user_data;
   SLang_Name_Type *function = NULL;
   SLang_Any_Type *conn_obj;

   if (data->function) {
      function = data->function;
   } else {
      /* Make a copy of handler_name so that the compiler doesn't complain
       * that we're passing a const pointer to a function (SLang_get_function)
       * that takes a non-const argument */
      handler = (char *)malloc((strlen(handler_name) + 1) * sizeof(char));
      strcpy(handler,handler_name);

      function = SLang_get_function(handler);

      if (function == NULL)
         SLang_vmessage("SLglade: WARNING: cannot find function matching name \"%s\"",
	   handler);

      free(handler);
   }

   if (function == NULL) {
      SLang_vmessage(
	"SLglade: WARNING: cannot connect handler function to signal \"%s\" on widget \"%s\"",
	signal_name,glade_get_widget_name((GtkWidget *)object));
   } else {
      if (connect_object == NULL) {
         g_signal_connect_closure(object, signal_name,
           slg_closure_new(function,data->args,data->nargs,NULL),after);
      } else {
         /* I guessed that connect_object should be the swap_data in the
	  * slGClosure based on an examination of the functions
	  * pyg_closure_new() in pygtype.c and pygobject_connect_object()
	  * in pygobject.c of the pygtk-2.0.0 distribution and the description
	  * of g_signal_connect_swapped() in the GTK+ API docs.  This seems to
	  * work, so I think I'm right. */

         /* If necessary, we can safely assume that object is a GtkWidget, as
	  * (GladeXML*)self->priv->name_hash contains only GtkWidgets (see the
	  * libglade implementation of glade_xml_get_widget()). */

         /* The following is rather hackish (pushing the GObject on to the stack
	  * and then immediately popping it back off), but I couldn't figure out
	  * another way to create a S-Lang GObject from connect_object.  I thought
	  * I'd be able to do something like
	  *
          *   connect_object_mmt = create_opaque_mmt(GObject_Type, connect_object, 0);
	  *
	  * and then pass (SLang_Any_Type *)connect_object_mmt to slg_closure_new(),
	  * but that ended up producing a "Type not registered" error from
	  * _SLclass_get_class. */

         /* Probably ought to check for failure here, but I don't know how
	  * this could fail */
         GObject_Type_push(connect_object, GObject_Type);
	 pop_anytype_or_null(&conn_obj);

         g_signal_connect_closure(object, signal_name,
           slg_closure_new(function,data->args,data->nargs,conn_obj),
	   (after ? G_CONNECT_AFTER : 0) | G_CONNECT_SWAPPED);
      }
   }
}

static void slglade_xml_signal_connect(void)
{
   Slirp_Opaque *xml_self;
   char *handlername = NULL;
   SLang_Ref_Type *function_ref = NULL;
   signal_connect_data data;

   SLang_MMT_Type *mmt = NULL;
   SLang_Name_Type *function;
   SLang_Any_Type **args;
   unsigned int nargs;

   if (SLang_Num_Function_Args < 3) {
      SLang_verror (SL_USAGE_ERROR,
        "Usage: glade_xml_signal_connect(GladeXML,String,function_ref[,data,...]);");
      return;
   }

   nargs = SLang_Num_Function_Args - 3;

   if (extract_slang_args(nargs,&args) == -1 ||
         SLang_pop_ref(&function_ref) == -1 ||
         SLang_pop_slstring(&handlername) == -1 ||
	 SLang_pop_opaque(GladeXML_Type, &xml_self, &mmt) == -1 ||
         NULL == (function = SLang_get_fun_from_ref(function_ref))) {
      SLang_verror (SL_INTRINSIC_ERROR,
        "Unable to validate arguments to: glade_xml_signal_connect");
   } else {
      data.function = function;
      data.args = args;
      data.nargs = nargs;

      glade_xml_signal_connect_full(SAFE_DEREF_OPAQUE(xml_self), handlername,
        signal_connect_connect_func, &data);
   }

   SLang_free_mmt(mmt);
   SLang_free_slstring(handlername);
   SLang_free_ref(function_ref);
}

static void slglade_xml_signal_autoconnect(void)
{
   Slirp_Opaque *xml_self;
   signal_connect_data data;

   SLang_MMT_Type *mmt = NULL;

   if (SLang_Num_Function_Args != 1) {
      SLang_verror (SL_USAGE_ERROR,
        "Usage: glade_xml_signal_autoconnect(GladeXML);");
      return;
   }

   if (SLang_pop_opaque(GladeXML_Type, &xml_self, &mmt) == -1) {
      SLang_verror (SL_INTRINSIC_ERROR,
        "Unable to validate arguments to: glade_xml_signal_autoconnect");
      return;
   }

   data.function = NULL;
   data.args = NULL;
   data.nargs = 0;

   glade_xml_signal_autoconnect_full(SAFE_DEREF_OPAQUE(xml_self),
     signal_connect_connect_func, &data);

   SLang_free_mmt(mmt);
}

static void slglade_xml_new_from_buffer (void)
{
   GladeXML* rtn;
   char* arg1;
   char* arg2;
   char* arg3;

   if (SLang_Num_Function_Args != 3) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: GladeXML = glade_xml_new_from_buffer(String,String,String);");
	return;
   }
   if (pop_obj_or_null(SLANG_STRING_TYPE,(void**)&arg3) == -1 ||
	pop_obj_or_null(SLANG_STRING_TYPE,(void**)&arg2) == -1 ||
	SLANG_STRING_TYPE_pop(&arg1, NULL) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_xml_new_from_buffer");
	return;
   }
   rtn =    glade_xml_new_from_buffer(
			arg1,
			(int)strlen(arg1),
			arg2,
			arg3);
   (void) GladeXML_Type_push(rtn, GladeXML_Type);
}

static void slglade_xml_get_widget_prefix (void)
{
   /* This becomes _glade_xml_get_widget_prefix() in S-Lang space and
    * is wrapped by glade_xml_get_widget_prefix(), which creates the array */

   GList* list;
   Slirp_Opaque* arg1 = NULL;
   SLang_MMT_Type *mmt_arg1 = NULL;
   char* arg2;

   char* empty_str = "";
   int list_length = 0;

   if (SLang_Num_Function_Args != 2) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: GtkWidget[] = glade_xml_get_widget_prefix(GladeXML,String);");
	return;
   }
   if (pop_obj_or_null(SLANG_STRING_TYPE,(void**)&arg2) == -1 ||
	SLang_pop_opaque(GladeXML_Type, &arg1, &mmt_arg1) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_xml_get_widget_prefix");
	return;
   }

   if (arg2 == NULL)  arg2 = empty_str;

   list =    glade_xml_get_widget_prefix(
			 SAFE_DEREF_OPAQUE(arg1),
			arg2);
   SLang_free_mmt(mmt_arg1);

   if (list == NULL) {
      SLang_push_null();
      return;
   }

   /* Since I can't figure out how to create an array of MMTs, we just
    * push the widgets and count onto the stack and let the user-space
    * function glade_xml_get_widget_prefix() roll them into an array */

   list = g_list_reverse(list);

   while (list != NULL) {
      GtkWidget_Type_push((GtkWidget *)list->data, GtkWidget_Type);
      list = list->next;
      list_length++;
   }

   (void) SLANG_INT_TYPE_push(list_length, NULL);
}



/*
 * The remaining functions were automatically generated by SLIRP
 */

static void slglade_require (void)
{
   char* arg1;

   if (SLang_Num_Function_Args != 1) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: glade_require(String);");
	return;
   }
   if (SLANG_STRING_TYPE_pop(&arg1, NULL) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_require");
	return;
   }
   glade_require(
			arg1);
}

static void slglade_xml_new (void)
{
   GladeXML* rtn;
   char* arg1;
   char* arg2;
   char* arg3;

   if (SLang_Num_Function_Args != 3) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: GladeXML = glade_xml_new(String,String,String);");
	return;
   }
   if (pop_obj_or_null(SLANG_STRING_TYPE,(void**)&arg3) == -1 ||
	pop_obj_or_null(SLANG_STRING_TYPE,(void**)&arg2) == -1 ||
	SLANG_STRING_TYPE_pop(&arg1, NULL) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_xml_new");
	return;
   }
   rtn =    glade_xml_new(
			arg1,
			arg2,
			arg3);
   (void) GladeXML_Type_push(rtn, GladeXML_Type);
}

static void slglade_xml_get_widget (void)
{
   GtkWidget* rtn;
   Slirp_Opaque* arg1 = NULL;
   SLang_MMT_Type *mmt_arg1 = NULL;
   char* arg2;

   if (SLang_Num_Function_Args != 2) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: GtkWidget = glade_xml_get_widget(GladeXML,String);");
	return;
   }
   if (SLANG_STRING_TYPE_pop(&arg2, NULL) == -1 ||
	SLang_pop_opaque(GladeXML_Type, &arg1, &mmt_arg1) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_xml_get_widget");
	return;
   }
   rtn =    glade_xml_get_widget(
			 SAFE_DEREF_OPAQUE(arg1),
			arg2);
   SLang_free_mmt(mmt_arg1);
   (void) GtkWidget_Type_push(rtn, GtkWidget_Type);
}

static void slglade_get_widget_name (void)
{
   const char* rtn;
   Slirp_Opaque* arg1 = NULL;
   SLang_MMT_Type *mmt_arg1 = NULL;

   if (SLang_Num_Function_Args != 1) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: String = glade_get_widget_name(GtkWidget);");
	return;
   }
   if (SLang_pop_opaque(GtkWidget_Type, &arg1, &mmt_arg1) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_get_widget_name");
	return;
   }
   rtn =    glade_get_widget_name(
			 SAFE_DEREF_OPAQUE(arg1));
   SLang_free_mmt(mmt_arg1);
   (void) SLANG_STRING_TYPE_push(( char*)rtn, NULL);
}

static void slglade_get_widget_tree (void)
{
   GladeXML* rtn;
   Slirp_Opaque* arg1 = NULL;
   SLang_MMT_Type *mmt_arg1 = NULL;

   if (SLang_Num_Function_Args != 1) {
	SLang_verror (SL_USAGE_ERROR,
	   "Usage: GladeXML = glade_get_widget_tree(GtkWidget);");
	return;
   }
   if (SLang_pop_opaque(GtkWidget_Type, &arg1, &mmt_arg1) == -1 ) {
	SLang_verror (SL_INTRINSIC_ERROR,
	   "Unable to validate arguments to: glade_get_widget_tree");
	return;
   }
   rtn =    glade_get_widget_tree(
			 SAFE_DEREF_OPAQUE(arg1));
   SLang_free_mmt(mmt_arg1);
   (void) GladeXML_Type_push(rtn, GladeXML_Type);
}

